<?php
namespace TaskGroup;
use Cli\Output;
class RbacTasks extends Base {
  protected static $tasks = array(
    'generate_permissions'   => ''
   ,'create_permissions'     => ''
   ,'delete_permissions'     => ''
   ,'assign_permissions_to_role'   => ''
   ,'create_roles'           => ''
   ,'delete_roles'           => ''
   ,'create_user'            => ''
   ,'delete_user'            => ''
   ,'set_password'           => ''
   ,'send_new_password'      => ''
   ,'assign_user_to_roles'   => ''
   ,'remove_user_from_roles' => ''
   ,'list_users'             => ''
   ,'list_permissions'       => ''
   ,'toggle_public'          => ''
   ,'initialize'             => ''
  );
  
  public static function initialize($options) {
    $administrator_email = static::map($options, 0, true, "Missing first Parameter [adminsitrator_email:string] Example: administrator@example.com");
    // Generate permissions
    if(!static::generate_permissions($options)) return false;
    // Create Admin Role
    if(!static::create_roles(array('administrator rbac_administrator'))) return false;
    // Create Administrator
    if(!static::create_user(array('administrator', 'password', $administrator_email, 'administrator rbac_administrator'))) return false;
    // Send new Password
    if(!static::send_new_password(array('administrator'))) return false;
    // Assign permission_controller::* Permissions to the RBAC Admin Role
    if(!static::assign_permissions_to_role(array('permission_controller::*', 'rbac_administrator'))) return false;
    // Assign role_controller::* Permissions to the RBAC Admin Role
    if(!static::assign_permissions_to_role(array('role_controller::*', 'rbac_administrator'))) return false;
    // Assign user_controller::* Permissions to the RBAC Admin Role
    if(!static::assign_permissions_to_role(array('user_controller::*', 'rbac_administrator'))) return false;
    // Make user_controller::forgot_password public
    // Make user_controller::login public
    if(!static::toggle_public(array(true, 'user_controller::login user_controller::signup user_controller::forgot_password'))) return false;
    return true;
  }
  
  public static function send_new_password($options) {
    $logins = explode(' ', static::map($options, 0, true, "Missing first Parameter [logins:string] Example: \"john jane administrator\""));
    $errors = false;
    foreach($logins as $login) {
      if(!$u = \User::find_by_login($login)) {
        echo "\r\nCould not find user {$login}";
        $errors = true;
        continue;
      }
      if(!$u->send_new_password()) {
        echo "\r\nCould not send new password to user {$login}";
        $errors = true;
        continue;      
      }
    }
    return !$errors;
  }  
  
  public static function generate_permissions($options) {
    $errors = false;
    foreach(\Roboframe\Base::list_application_controller_files() as $controller_dir_entry) {
      $controller = str_replace('.php', '', $controller_dir_entry);
      if($controller == 'application_controller') { 
        continue; 
      }
      foreach(\Roboframe\Base::list_controller_actions($controller_dir_entry) as $action) {
        $permission_name = $controller."::".$action;  
        if(\Permission::find_by_name($permission_name)) {
          echo "\r\nAlready exists {$permission_name}";
        } else {
          $p = new \Permission(array('name' => $permission_name));
          if($p->save()) {
            echo "\r\nCreated {$permission_name}";
          } else {
            echo "\r\nFailed creating {$permission_name}:";
            foreach($p->errors as $e) {
              echo "\r\n{$e}";
            }
            $errors = true;
          }
        }
      }
    }
    return !$errors;
  }
  
  public static function create_permissions($options) {
    $permission_names = static::map($options, 0, true, "Missing first Parameter [permission_names:string] Example: \"access_secret remove_users\"");
    
    $errors = false;
    foreach(explode(' ', $permission_names) as $permission_name) {
      $permission = array('name' => $permission_name);
      if($p = \Permission::find_by_name($permission['name'])) {
        echo "\r\nAlready exists {$p->name}";
        $errors = true;
        continue;
      }
      
      $p = new \Permission($permission);
      if(!$p->save()) {
        echo "\r\nFailed creating permission {$p->name}:";
        foreach($p->errors as $e) {
          echo "\r\n{$e}";
        }
        $errors = true;
        continue;
      }
      echo "\r\nCreated permission {$p->name}";
      continue;
    }
    
    if($errors) return false;
    return true;
  }
  
  public static function create_roles($options) {
    $role_names = static::map($options, 0, true, "Missing first Parameter [role_names:string] Example: \"default_user manager employee\"");
    
    $errors = false;
    foreach(explode(' ', $role_names) as $role_name) {
      $role = array('name' => $role_name);
      if($r = \Role::find_by_name($role['name'])) {
        echo "\r\nAlready exists {$r->name}";
        $errors = true;
        continue;
      }
      
      $r = new \Role($role);
      if(!$r->save()) {
        echo "\r\nFailed creating role {$r->name}:";
        foreach($r->errors as $e) {
          echo "\r\n{$e}";
        }
        $errors = true;
        continue;
      }
      echo "\r\nCreated role {$r->name}";
      continue;
    }
    return !$errors;
  }
  
  public static function create_user($options) {
    $user['login'] = static::map($options, 0, true, "Missing first Parameter [login:string] Example: john");
    $user['password'] = $user['password_confirmation'] = static::map($options, 1, true, "Missing second Parameter [password:string] Example: very_secure_password");
    $user['email'] = static::map($options, 2, true, "Missing second Parameter [email:string] Example: john@foo.com");
    $roles = static::map($options, 3, false, "Missing third Parameter [roles:string] Example: \"user administrator sudoers\"");
    
    $errors = false;
    
    if($u = \User::find_by_login($user['login'])) {
      echo "\r\nUser already exists {$u->login}";
    } else {
      $u = new \User($user);
      if($u->save()) {
        echo "\r\nCreated {$u->login}";
      } else {
        $errors = true;
        echo "\r\nFailed creating {$u->login}:";
        foreach($u->errors as $e) {
          echo "\r\n{$e}";
        }
        return false;
      }

      //$roles = (is_string($roles)) ? array($roles) : $roles;
      $roles = explode(' ', $roles);
      if(isset($roles) && is_array($roles)) {
        foreach($roles as $role) {
          if($r = \Role::find_by_name(trim($role))) {
            if($ur = \UserRole::create(array('user_id' => $u->id, 'role_id' => $r->id))) {
              echo "\r\nAssigned Role {$r->name}";
            } else {
              $errors = true;
              echo "\r\nCould not assign Role {$r->name}";
            }
          } else {
            $errors = true;
            echo "\r\nCould not find role {$role}";
          }
        }
      } else {
        echo "\r\nNo roles to add";
      }
    }
    return !$errors;
  }
  
  public static function delete_permissions($options) {
    $permission_names = explode(' ', static::map($options, 0, true, "Missing first Parameter [permissions:string] Example: \"create_user view_secret welcome\"or \"user*\""));
    $delete_role_permissions = static::map($options, 1, true, "Missing second Parameter [delete_role_permissions:boolean]");

    $errors = false;
    
    if(count($permission_names) == 1 && strstr(($permission_name = $permission_names[0]), '*')) {
      $permission_name = str_replace('*', '%', $permission_name);
      $permissions = \Permission::find('all', array('conditions' => "name LIKE '{$permission_name}'"));
    }
    
    if((count($permission_names) == 1 && !strstr(($permission_name = $permission_names[0]), '*')) || count($permission_names) > 1) {
      $permissions = \Permission::find('all', array('conditions' => array('name in (?)', $permission_names)));
    }

    if(!count($permissions)) {
      echo "\r\nNo permissions found";
      return false;
    }
    
    
    foreach($permissions as $p) {
      $role_permissions = $p->role_permissions;
      if(!$p->delete()) {
        echo "\r\nCould not delete permission {$p->name}";
        $errors = true;
        continue;    
      }
      
      if($delete_role_permissions) {
        foreach($role_permissions as $rp) {
          if($rp->delete()) {
            echo "\r\nDeleted role permission {$p->name} <=> {$rp->role->name}";
          } else {
            echo "\r\nCould not delete role permission {$p->name} <=> {$rp->role->name}";
            $errors = true;
          }
        }
      }
      echo "\r\nDeleted permission {$p->name}";
    }
    return !$errors;   
  }
  
  public static function delete_roles($options) {
    $role_names = explode(' ', static::map($options, 0, true, "Missing first Parameter [role_names:string] Example: \"administrator manager user \"or \"admin*\""));
    $delete_user_roles = static::map($options, 1, true, "Missing second Parameter [delete_user_roles:boolean]");
    $delete_role_permissions = static::map($options, 2, true, "Missing third Parameter [delete_role_permissions:boolean]");
    
    $errors = false;
    
    if(count($role_names) == 1 && strstr(($role_name = $role_names[0]), '*')) {
      $role_name = str_replace('*', '%', $role_name);
      $roles = \Role::find('all', array('conditions' => "name LIKE '{$role_name}'"));
    }
    
    if((count($role_names) == 1 && !strstr(($role_name = $role_names[0]), '*')) || count($role_names) > 1) {
      $roles = \Role::find('all', array('conditions' => array('name in (?)', $role_names)));
    }

    if(!count($roles)) {
      echo "\r\nNo Roles found";
      return false;
    }
    
    
    foreach($roles as $r) {
      $user_roles = $r->user_roles;
      $role_permissions = $r->role_permissions;
      
      if(!$r->delete()) {
        echo "\r\nCould not delete role {$r->name}";
        $errors = true;
        continue;    
      }
      
      if($delete_user_roles) {
        foreach($user_roles as $ur) {
          if($ur->delete()) {
            echo "\r\nDeleted user role {$r->name} <=> {$ur->user->login}";
          } else {
            echo "\r\nCould not delete user role {$r->name} <=> {$ur->user->login}";
            $errors = true;
          }
        }
      }
      
      if($delete_role_permissions) {
        foreach($role_permissions as $rp) {
          if($rp->delete()) {
            echo "\r\nDeleted role permission {$r->name} <=> {$rp->permission->name}";
          } else {
            echo "Could not delete role permission {$r->name} <=> {$rp->permission->name}";
            $errors = true;
          }
        }
      }
      echo "\r\nDeleted role {$r->name}";
    }
    return !$errors;
  }
  
  public static function delete_user($options) {
    $user['login'] = static::map($options, 0, true, "Missing first Parameter [login:string]");
    $delete_user_roles = static::map($options, 1, true, "Missing second Parameter [delete_user_roles:boolean]");
    
    $errors = false;
    
    if(!$u = \User::find_by_login($user['login'])) {
      echo "\r\nCould not find user {$user['login']}";
      return false;
    }
    
    $user_roles = $u->user_roles;
    if(!$u->delete()) {
      echo "\r\nCould not delete user {$u->login}";
      return false;    
    }
    
    if($delete_user_roles) {
      foreach($user_roles as $ur) {
        if($ur->delete()) {
          echo "\r\nDeleted user role {$u->login} <=> {$ur->role->name}";
        } else {
          $errors = true;
          echo "Could not delete user role {$u->login} <=> {$ur->role->name}";
        }
      }
    }
    echo "\r\nDeleted user {$u->login}";
    return !$errors;
  }
  
  public static function assign_permissions_to_role($options) {
    $permission_names = explode(' ', static::map($options, 0, false, "Missing first Parameter [permissions:string] Example: \"user administrator sudoers\" or \"admin*\""));
    $role_name = static::map($options, 1, true, "Missing second Parameter [role_name:string]");
    
    $errors = false;
    
    if(!$r = \Role::find_by_name($role_name)) {
      echo "\r\nCould not find role {$role_name}";
      return false;
    }
    //var_dump(count($permission_names)); var_dump(strstr(($permission_name = $permission_names[0]), '*')); var_dump($permission_name);
    if(count($permission_names) == 1 && strstr(($permission_name = $permission_names[0]), '*')) {
      $permission_name = str_replace('*', '%', $permission_name);
      $permissions = \Permission::find('all', array('conditions' => "name LIKE '{$permission_name}'"));
    }
    
    if((count($permission_names) == 1 && !strstr(($permission_name = $permission_names[0]), '*')) || count($permission_names) > 1) {
      $permissions = \Permission::find('all', array('conditions' => array('name in (?)', $permission_names)));
    }

    if(!count($permissions)) {
      echo "\r\nNo permissions found";
      return false;
    }    
    
    foreach($permissions as $p) {
      if(\RolePermission::find_by_role_id_and_permission_id($r->id, $p->id)) {
        echo "\r\nRolePermission already exists {$r->name} <=> {$p->name}";
        $errors = true;
        continue;
      }
      $rp = new \RolePermission();
      $rp->role_id = $r->id;
      $rp->permission_id = $p->id;
      if(!$rp->save()) {
        $errors = true;
        echo "\r\nCould not save RolePermission:";
        foreach($rp->errors as $e) {
          echo "\r\n{$e}";
        }
        continue;
      }
      echo "\r\nSaved RolePermission {$r->name} <=> {$p->name}"; 
    }
    
    
    return !$errors;
  }
  
  public static function assign_user_to_roles($options) {
    $user['login'] = static::map($options, 0, true, "Missing first Parameter [login:string]");
    $role_names = static::map($options, 1, false, "Missing second Parameter [roles:string] Example: \"user administrator sudoers\"");
    
    $errors = false;
    
    if(!$u = \User::find_by_login($user['login'])) {
      echo "\r\nCould not find user {$user['login']}";
      return false;
    }
    
    foreach(explode(' ', $role_names) as $role_name) {
      if(!$r = \Role::find_by_name($role_name)) {
        echo "\r\nCould not find role {$role_name}";
        $errors = true;
        continue;
      }
      /*      
      $u->assign_role($r);
      if(!$u->save()) {
        echo "\r\nCould not save user with role {$u->login} <=> {$r->name}";
        $errors = true;
        continue;
      } 
      echo "\r\nSaved user with role {$u->login} <=> {$r->name}";
      */
      if($ur = \UserRole::find_by_user_id_and_role_id($u->id, $r->id)) {
        echo "\r\nUser role already exists {$u->login} <=> {$r->name}";
        $errors = true;
        continue;
      }
      
      $ur = new \UserRole();
      $ur->user_id = $u->id;
      $ur->role_id = $r->id;
      if(!$ur->save()) {
        echo "\r\nCould not save user role {$u->login} <=> {$r->name}";
        $errors = true;
        continue;
      } 
      echo "\r\nSaved user role {$u->login} <=> {$r->name}";

    }
    return !$errors;
  }
  
  public static function list_permissions($options) {
    $output = array();
    //$output[] = array_flip(\Role::collection_to_array(\Role::find('all'), 'name'));
    $role_names = \Role::collection_to_array(\Role::find('all'), 'name');
    $i = 0;
    if(!$permissions = \Permission::find('all')) {
      $output[$i]['Permission'] = null;
      $output[$i]['Public'] = null;
      foreach($role_names as $role_name) {
        $output[$i][$role_name] = null;
      }
    } else {
      foreach($permissions as $p) {
        //var_dump($u->login);
        $output[$i]['Permission'] = $p->name;
        $output[$i]['Public'] = ($p->public) ? 'true' : 'false';
        $role_permission_names = \RolePermission::collection_to_array($p->roles, 'name');
        foreach($role_names as $role_name) {
          if(in_array($role_name, $role_permission_names)) {
            $output[$i][$role_name] = 'x';
          } else {
            $output[$i][$role_name] = '-';
          }
        }
        $i++;
      }
    }

    \Cli\Output::array_to_table($output);
    return true;
  }
  
  public static function list_users($options) {
    $output = array();
    //$output[] = array_flip(\Role::collection_to_array(\Role::find('all'), 'name'));
    $role_names = \Role::collection_to_array(\Role::find('all'), 'name');
    $i = 0;
    if(!$users = \User::find('all')) {
      $output[$i]['User'] = null;
      foreach($role_names as $role_name) {
        $output[$i][$role_name] = null;
      }
    } else {
      foreach($users as $u) {
        //var_dump($u->login);
        $output[$i]['User'] = $u->login;
        $user_role_names = \UserRole::collection_to_array($u->roles, 'name');
        foreach($role_names as $role_name) {
          if(in_array($role_name, $user_role_names)) {
            $output[$i][$role_name] = 'x';
          } else {
            $output[$i][$role_name] = '-';
          }
        }
        $i++;
      }
    }
/*
    if(count($role_names) == 0) {
      echo "\r\nNo roles defined";
      return true;      
    }
*/    
    \Cli\Output::array_to_table($output);
    return true;
  }
  
  public static function set_password($options) {
    $user['login'] = static::map($options, 0, true, "Missing first Parameter [login:string]");
    $user['password'] = $user['password_confirmation'] = static::map($options, 1, true, "Missing second Parameter [password:string]");
    
    if(!$u = \User::find_by_login($user['login'])) {
      echo "\r\nCould not find user {$user['login']}";
      return false;
    }
    
    $u->password = $user['password'];
    $u->password_confirmation = $user['password_confirmation'];
    
    if(!$u->save()) {
      echo "\r\nFailed saving {$u->login}:";
      foreach($u->errors as $e) {
        echo "\r\n{$e}";
      }
      return false;
      
    } else {
      echo "\r\nSaved {$u->login}";
      return true;
    }
  }

  public static function remove_user_from_roles($options) {
    $user['login'] = static::map($options, 0, true, "Missing first Parameter [login:string]");
    $role_names = static::map($options, 1, false, "Missing second Parameter [roles:string] Example: \"user administrator sudoers\"");
    
    $errors = false;
    
    if(!$u = \User::find_by_login($user['login'])) {
      echo "\r\nCould not find user {$user['login']}";
      return false;
    }
    
    foreach(explode(' ', $role_names) as $role_name) {
      if(!$r = \Role::find_by_name($role_name)) {
        echo "\r\nCould not find role {$role_name}";
        $errors = true;
        continue;
      }
      if(!$ur = \UserRole::find_by_user_id_and_role_id($u->id, $r->id)) {
        echo "\r\nCould not find user role {$u->login} <=> {$r->name}";
        $errors = true;
        continue;      
      }
      if(!$ur->delete()) {
        echo "\r\nCould not delete user role {$u->login} <=> {$r->name}";
        $errors = true;
        continue;      
      }
      echo "\r\nDeleted user role {$u->login} <=> {$r->name}";
    }
    return !$errors;
  }
  
  public static function toggle_public($options) {
    $public = static::map($options, 0, true, "Missing first Parameter [public:boolean]");
    $permission_names = explode(' ', static::map($options, 1, false, "Missing second Parameter [permissions:string] Example: \"create_user view_secret welcome\"or \"user*\""));
    
    $errors = false;
    $public = (boolean)$public;
    
    if(count($permission_names) == 1 && strstr(($permission_name = $permission_names[0]), '*')) {
      $permission_name = str_replace('*', '%', $permission_name);
      $permissions = \Permission::find('all', array('conditions' => "name LIKE '{$permission_name}'"));

    }
    
    if((count($permission_names) == 1 && !strstr(($permission_name = $permission_names[0]), '*')) || count($permission_names) > 1) {
      $permissions = \Permission::find('all', array('conditions' => array('name in (?)', $permission_names)));
    }

    if(!count($permissions)) {
      echo "\r\nNo permissions found";
      return false;
    }
    
    foreach($permissions as $p) {
      $p->public = $public;
      if(!$p->save()) {
        echo "\r\nFailed saving {$p->name}:";
        foreach($p->errors as $e) {
          echo "\r\n{$e}";
        }
        $errors = true;
        continue;
      }
      echo "\r\nSaved permission {$p->name}";
    }    
    return !$errors;
  }
}